package com.eight.socket;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.eight.pojo.ChatData;
import com.eight.service.ChatService;
import com.eight.util.TimeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description: TODO
 * @Author: Van.Planifolia
 * @CreateTime: 2023-01-09  13:12
 * @Version: 1.0
 */
@ServerEndpoint(value = "/chat/chatserver/{username}")
@Component
public class WebSocketServer {
    /**
     * 注入业务层
     */

    private static ChatService chatService;

    /**
     * 在webSocket中必须使用构造器注入否则会空指针
     * @param chatService service对象
     */
    @Autowired
    public WebSocketServer(ChatService chatService){
        WebSocketServer.chatService=chatService;
    }

    public WebSocketServer() {
    }

    /**
     * Logger的静态常量
     */
    private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
    /**
     * 使用线程安全的HashMap来实现记录聊天Session
     */
    public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 当用户进入聊天室的时候
     * @param session 进入的用户的session对象
     * @param username 用户的id
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("username") String username){
        // 当有人进入的时候我们就将session以及用户信息存入到Map中
        sessionMap.put(username,session);
        // 日志输出
        log.info("用户->"+username+" 进入了房间");
        // 将用户信息广播到全部的用户列表上客户端,不过我们要构建Json。格式为
        // {{ "user":["username":"name1","username":"name2"]}}
        // 最外层json对象
        JSONObject result = new JSONObject();
        // 内层json数组，我们要开始遍历map了
        JSONArray tempArray = new JSONArray();
        sessionMap.keySet().forEach(e->{
            JSONObject tempObj = new JSONObject();
            tempObj.set("username",e);
            tempArray.add(tempObj);
        });
        // 然后将数组放入外层对象中
        result.set("users",tempArray);
        // 最后将数据推送出去
        sendAllMessage(JSONUtil.toJsonStr(result));
    }

    /**
     * 当有人下线的时候
     * @param session 下线用户的session对象
     * @param username 下线用户的名字
     */
    @OnClose
    public void onClose(Session session,@PathParam("username") String username){
        // 直接把这个人从map中删除就ok了
        sessionMap.remove(username);
        log.info("用户-> "+username+" 下线了！");
    }

    /**
     * 当接收到消息的时候
     * @param message 发过来的消息实体
     * @param session 发送消息的对象Session
     * @param username 发送消息的名字
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("username") String username){
        // 我们解析message对象，从中获取发送消息的目标。这一步就要求我们前端传递过来的信息的json格式中必须有这一列
        log.info("服务端收到用户username={}的消息:{}", username, message);
        JSONObject msg = JSONUtil.parseObj(message);
        // 目标用户
        String to = msg.getStr("to");
        // 消息正文
        String text = msg.getStr("text");
        // 获取目标session对象
        Session toSession = sessionMap.get(to);
        if (toSession!=null){
            // 组装消息
            JSONObject msgObj = new JSONObject();
            msgObj.set("from",username);
            msgObj.set("text",text);
            // 发送消息之前我们执行添加操作
            ChatData chatData = new ChatData().setMsg(text).setTos(to).setFroms(username).setSendtime(TimeUtil.now(""));
            chatService.save(chatData);
            // 发送消息
            sendMessage(msgObj.toString(),toSession);
            log.info("发送给用户username={}，消息：{}", to, msgObj);
        }else {
            log.info("发送失败，未找到用户username={}的session", to);
        }
    }

    /**
     * 发生错误的回调
     * @param session 调用者的Session
     * @param error 错误信息
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 推送至全部客户端
     * @param message 被推送的消息
     */
    private void sendAllMessage(String message){
        // 因为我们要给全部的session对象发送消息，所以这里要循环map
        sessionMap.values().forEach(e->{
            try {
                e.getBasicRemote().sendText(message);
                log.info("消息发送成功！");
            } catch (IOException ex) {
                log.error("消息发送失败，错误信息为 -> \n"+ex.getMessage());
            }
        });
    }

    /**
     * 给指定的用户发信息
     * @param message 发送的信息详情
     * @param toSession 指定的用户Session对象
     */
    private void sendMessage(String message,Session toSession){
        // 我们这里是给指定的用户发送消息，所以就直接调用传递过来的对象session发送消息即可
        try {
            toSession.getBasicRemote().sendText(message);
            log.info("消息发送成功");
        } catch (IOException e) {
            log.error("消息发送失败，错误信息为 -> \n"+e.getMessage());
        }
    }

}

